/**
 * @file    app_user.c
 * @author  Azolla (1228449928@qq.com)
 * @brief
 * @version 0.1
 * @date    2022-09-19
 * 
 * @copyright Copyright (c) 2022
 * */
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "esp_system.h"
#include "mbedtls/base64.h"

#include "ble_gap.h"
#include "ble_mesh_prov.h"
#include "ble_gatts.h"
#include "ble_gattc.h"
#include "ble_bind.h"

#include "wifi_init.h"
#include "wifi_sock.h"
#include "wifi_sntp.h"
#include "wifi_user.h"
#include "wifi_ota.h"
#include "wifi_mqtt.h"

#include "hal_rs485.h"
#include "hal_a7106.h"
#include "audio_board_init.h"
 
#include "app_user.h"
#include "app_scene.h"

#define APP_CONFIG_MQTT_ENABLE    1    // 1: 使能MQTT; 0: TCP/UDP 
#define APP_USER_DEBUG_ENABLE     1

#define TAG  "app_user"

/* 百度账号
 * 13860115663
 * yirui1234
 * */

#ifdef TAG
#define APP_USER_LOGI(format, ...)    ESP_LOGI(TAG, format, ##__VA_ARGS__)
#define APP_USER_LOGW(format, ...)    ESP_LOGW(TAG, format, ##__VA_ARGS__)
#define APP_USER_LOGE(format, ...)    ESP_LOGE(TAG, format, ##__VA_ARGS__)
#else
#define APP_USER_LOGI(format, ...)
#define APP_USER_LOGW(format, ...)
#define APP_USER_LOGE(format, ...)
#endif

//==============================================================================================================
//==============================================================================================================
static nvs_wifi_t nvs_wifi;
static self_info_t self;
 
typedef struct {

    bool sys_reset;        // 1: 软件复位
    uint8_t audio_voice;   // 音频索引

    struct {
        a7106_id_t id;
        uint8_t  channel;
        uint8_t  inter;   // 接收中断信号
        uint8_t  buff[4]; // 发送buff
        uint8_t  reissue; // 补发次数
    } rf;
 
} app_run_t;
static app_run_t app = { 0 };

typedef struct {
    a7106_id_t rfid;
    uint8_t update;          // 1: 有更新
} nvs_user_t;
static nvs_user_t nvs_user;
 
esp_err_t nvs_user_handle(nvs_user_t *nvs, nvs_open_mode_t nvs_open_mode)
{
    return nvs_blob_handle((nvs_user_t *)nvs, sizeof(nvs_user_t), "user_key", nvs_open_mode);
}
 
static EventGroupHandle_t xEvent = NULL;  


static char mid_value[14];      // 消息ID字符串值；10位时间戳 + 3位编码
static char mid_ota_value[14];  // OTA时的消息ID值
 
//==============================================================================================================
//==============================================================================================================
static char *clear_handler(app_parse_data_t item);

//==============================================================================================================
//==============================================================================================================
 
void rf_motor_write(const uint8_t *data, uint8_t reissue)
{
    app.rf.reissue = reissue;
    memcpy(app.rf.buff, data, 4);
    //a7106_write_data(data, 4);  // RX -> STANDBY -> TX
}

static void app_a7106_handler(void)
{
    if (!gpio_get_level(3)) {  // 按住RF对码键
        if (app.rf.id.u32 != A7105_ID_CFIG) {
            app.rf.id.u32  = A7105_ID_CFIG;
            app.rf.channel = A7105_CH_CFIG;
            a7106_write_id(app.rf.id);
            a7106_set_channel(app.rf.channel);
            a7106_write_strobe(A7105_STROBE_RX); 
            APP_USER_LOGI("A7105_STROBE_RX");
        } else if (app.rf.inter) {  // 有接收中断信号！
            app.rf.inter--;
            a7106_id_t id;
            a7106_read_data(id.u8, 4);  // 读数据
            esp_log_buffer_hex("RX", id.u8, 4);
            if (nvs_user.rfid.u32 != id.u32) {
                nvs_user.rfid.u32 = id.u32;
                nvs_user.update = true;
            }
        }
        return;
    } else if (app.rf.id.u32 != nvs_user.rfid.u32) {
        app.rf.id.u32  = nvs_user.rfid.u32;
        app.rf.channel = app.rf.id.u8[1];  // 通道值 = ID[1]
		app.rf.channel += (app.rf.channel % 2);  // if (app.rf.channel % 2) app.rf.channel += 1;
        a7106_write_id(app.rf.id);
        a7106_set_channel(app.rf.channel + 1);    // 发送通道; RX -> STANDBY -> TX
        a7106_write_strobe(A7105_STROBE_STANDBY); // 发送前先设置为STANDBY;
        APP_USER_LOGI("A7105_STROBE_TX");
        return;
    } else {  // 可发送状态
        if (app.rf.reissue) {
            app.rf.reissue--;
            a7106_write_data(app.rf.buff, 4);  // RX -> STANDBY -> TX
        }
    }
}
    
void exit_isr_callback(gpio_num_t gpio_num)
{
    app.rf.inter++;
}
//==============================================================================================================
//==============================================================================================================
#define KEY_RESET_FACTORY   0       // IO0
#define KEY_CLEAR_NETWORK   3       // IO3
static void key_scan_task(void)
{
    static uint8_t key_time = 0;
    static uint8_t key_value = 0;
    union8_t key_temp = { .value = 0 };
    key_temp.bits.bit0 = !gpio_get_level(KEY_RESET_FACTORY);
    key_temp.bits.bit1 = !gpio_get_level(KEY_CLEAR_NETWORK);
    // APP_USER_LOGI("KEY->key_temp = 0x%02x", key_temp.value);
    if (key_value && key_value == key_temp.value) { // 键值保持不变！
        if (++key_time == 80) {  // 5s
            if (key_temp.bits.bit0 == true) {  // IO0
                //esp_ota_reset_factory();  // 恢复到出厂固件
            } else if (key_temp.bits.bit1 == true) {   // IO21
                app_parse_data_t item;
                clear_handler(item); // 清除网络
            }
            app.sys_reset = true;  // 使能软复位
            APP_USER_LOGI("KEY->app.sys_reset = %d\r\n", key_value);
        }
    } else { // 键值发现了变化！
        if ((key_value && !key_temp.value) && (key_time > 1 && key_time < 10)) {  // 单击
            APP_USER_LOGI("KEY->Click = 0x%02x\r\n", key_value);
        }
        key_value = key_temp.value;
        key_time = 0;
    }
    return;
}

//=====================================================================================
//=====================================================================================
bool get_sys_config_network(void)  // LVGL显示用
{
    return nvs_wifi.status ? true : false;  // 是否配网成功了
}

void esp_free_heap_print(void)
{
    #if 1  // debug
    APP_USER_LOGI("Free heap, current: %ld, minimum: %ld", 
                esp_get_free_heap_size(), 
                esp_get_minimum_free_heap_size());  // 打印内存
    APP_USER_LOGI("MALLOC_CAP_SPIRAM = %d, MALLOC_CAP_INTERNAL = %d", 
                heap_caps_get_free_size(MALLOC_CAP_SPIRAM), 
                heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
    #endif
}

void app_user_handle(void)
{
 
    key_scan_task();

    app_a7106_handler();

    /* 用户信息有更新 */
    if (nvs_user.update) {
        nvs_user.update = false;
        nvs_user_handle(&nvs_user, NVS_READWRITE);   // 写用户信息
        APP_USER_LOGI("nvs_user_handle = 0x%08lx", nvs_user.rfid.u32);
    }
    
}


//=============================================================================================================== 
//=============================================================================================================== 
// 用户逻辑功能任务
static void app_user_task(void *arg)
{ 
    union8_t net_curr = { 0 };  // 当前网络状态
    union8_t net_last = { 0 };  // 上次网络状态
    #define NET_BLE_STATE       bits.bit0
    #define NET_WIFI_STATE      bits.bit1
    #define NET_SERVER_STATE    bits.bit2
    #define LED_WIFI_STATE      bits.bit3
    #define LED_SERVER_STATE    bits.bit4

    // audio_music_play_mp3(0);
    // vTaskDelay(2000);
    app.audio_voice = VOICE_COMM_WIFI_CONNECTING;  // 提示正在连接网络

    while (520) {
        //=============================================================================
        // START... 
        //=============================================================================
        vTaskDelay(100);  /* 主任务执行的周期/MS */

        #if 1
        static uint8_t freetime = 0;
        if (++freetime > 200) {
            freetime = 0;
            esp_free_heap_print();
        }
        #endif
 
        //=============================================================================
        app_user_handle();
 

        //=============================================================================
        /* OTA升级状态 */
        static int ota_plan_last = 0;
        static uint8_t ota_timecnt = 100;
        int ota_plan = wifi_ota_update_plan();
        if ((ota_plan != 0 && ++ota_timecnt >= 20) || ota_plan >= 100) {  // 正在OTA, 定时2S上报一次进度
            if (ota_plan_last != ota_plan) {  // 变化了再上报！
                ota_timecnt = 0;
                if (ota_plan_last == 0) {
                    app.audio_voice = VOICE_COMM_OTA;  // 提示OTA开始
                } else if (ota_plan >= 100) {
                    app.audio_voice = VOICE_COMM_OTA_OK;
                    app.sys_reset = true;  // 使能软复位
                } 
                ota_plan_last = ota_plan;
                #ifdef APP_USER_DEBUG_ENABLE  // debug
                APP_USER_LOGI("ota_plan: %d\n", ota_plan);
                #endif
                char ota_str[5];
                itoa(ota_plan, ota_str, 10);
                app_upper_cloud_format(nvs_wifi.addr, mid_ota_value, "ota", (char *)ota_str, 0);  // 发送OTA进度
            }
        }
 
        //=============================================================================
        
        //=============================================================================
        /* WIFI连接状态 */
        net_curr.NET_WIFI_STATE = wifi_connect_status(0);
        if (net_last.NET_WIFI_STATE != net_curr.NET_WIFI_STATE) {
            net_last.NET_WIFI_STATE = net_curr.NET_WIFI_STATE;
            if (net_curr.NET_WIFI_STATE == true) {  // WIFI刚连接上
                app.audio_voice = VOICE_COMM_WIFI_CONNECT_OK;
            } else {  // WIFI刚断开连接
                app.audio_voice = VOICE_COMM_WIFI_DISCONNECT;
            }
        }

        //=============================================================================
        /* 服务器连接状态 */
        net_curr.NET_SERVER_STATE = mqtt_connect_status(0);
        if (net_last.NET_SERVER_STATE != net_curr.NET_SERVER_STATE) {
            net_last.NET_SERVER_STATE = net_curr.NET_SERVER_STATE;
            if (net_curr.NET_SERVER_STATE == true) {   // MQTT连接成功 
                app.audio_voice = VOICE_COMM_SERVER_CONNECT_OK;  // 每次连接上MQTT时，都上报一次版本号！
                vTaskDelay(500);
                app_upper_cloud_format(nvs_wifi.addr, "0", "ver", (char *)self.version,  0);
            } else { // MQTT刚断开连接
                app.audio_voice = VOICE_COMM_SERVER_DISCONNECT;
            } 
        } 
        /* 网络指示灯 */
        if (net_curr.NET_WIFI_STATE) {
            net_curr.LED_WIFI_STATE = 1;
            if (net_last.NET_SERVER_STATE) {
                net_curr.LED_SERVER_STATE = 1;
            } else {
                net_curr.LED_SERVER_STATE = !net_curr.LED_SERVER_STATE;
            }
        } else {
            net_curr.LED_WIFI_STATE = !net_curr.LED_WIFI_STATE;
            net_curr.LED_SERVER_STATE = 0;
        }
        gpio_set_level(GPIO_NUM_36, net_curr.LED_WIFI_STATE);
        gpio_set_level(GPIO_NUM_37, net_curr.LED_SERVER_STATE);

        //=============================================================================
        /* 软复位 */
        static uint8_t reset_time = 0;
        if (app.sys_reset == true) {
            if (++reset_time == 3) {
                bluedroid_stack_deinit();
                app_mqtt_client_disconnect();  // 断开MQTT连接
            } else if (reset_time > 6) { 
                APP_USER_LOGI("\n\nesp_restart...\n\n");
                esp_restart();              // 系统软复位！
            }
        }

        //=============================================================================
        //=============================================================================
        /* 语音播报！ */
        if (app.audio_voice) {
            audio_music_play_audio(app.audio_voice); 
            app.audio_voice = 0;
        }
        //========================================================================================
        // END...
    }
}

//=============================================================================================================== 
//=============================================================================================================== 
 
//===================================================================================
//===================================================================================
//===================================================================================
static char *ver_handler(app_parse_data_t item)
{
    return self.version;
}

// {"addr":"68b6b3341e7a","clear":"all"}
static char *clear_handler(app_parse_data_t item)
{
    if (get_sys_config_network()) {
        nvs_wifi.status = WIFI_CONFIG_NULL;
        app.audio_voice = VOICE_COMM_SERVER_DISCONNECT;       
    }
    nvs_wifi_reset();          // 清除配网信息！ 
    app.sys_reset = true;   // 使能软复位
    return "ok";
}

static char *ota_handler(app_parse_data_t item)
{
    strcpy(mid_ota_value, mid_value);  // 复制OTA的消息ID
    app_ota_start(item.mac, (char *)item.value);  // 启动OTA
    return NULL;
}
 
static char *sntp_handler(app_parse_data_t item)
{
    // {"addr":"000000000000","sntp":"1680161030"}
    char *value = (char *)item.value;
    value[11] = '\0';
    sntp_sync_time_custom( atoi(value) + 1 ); // 校准时间（+1s网关延时）
    return NULL;
}

// {"bind":"<MAC>|<NAME>"} 
// {"bind":"ok/fail"}    
static char *bind_handler(app_parse_data_t item)
{
    char *value = (char *)item.value;
 
    value[12] = '\0';  // '|' ===>>> '\0'
    uint8_t addr[6];
    uint16_t uniaddr = 0x00;
    mac_utils_str2hex(value, addr); // 设备地址
    if (value[13] == 'Y' && value[14] == 'i') {  // BLE-MESH
        uniaddr = ble_mesh_prov_bind_add(addr, value + 13);  // 添加绑定并更新
    }  
    if (uniaddr > 0) {
        sprintf(value, "0x%04X", uniaddr); 
        return value; 
    }
    return "fail";    
}

static char *unbind_handler(app_parse_data_t item)
{
    char *value = (char *)item.value;

    if (strcmp(value, "all") == 0) {  // 全部解绑
        ble_mesh_prov_unbind_delete_all();  // 全部解绑
        app.sys_reset = true;   // 使能软复位
    } else {
        uint16_t uniaddr = strtol(value, NULL, 16);
        uniaddr = ble_mesh_prov_unbind_delete(uniaddr);  // 删除绑定设备
        if (uniaddr == 0) return "fail";
    }
    return "ok";
}

// 电动床控制RF
static char *bed_handler(app_parse_data_t item)
{
    ARRAY_TYPE *value = (ARRAY_TYPE *)item.value;  // 数组格式
    uint8_t reissue;
    if (value[0] == 0x55 && value[1] == 0x55 && value[2] <= 0xaa) {  // 长按角度控制
        reissue = 6;   // 角度控制，需要连发
    } else {
        reissue = 1;   // 模式控制，只需一次
    }
    rf_motor_write(value, reissue);
    return "ok";
}


// mesh云端数据处理
static char *mesh_handler(app_parse_data_t item)
{
    bool ret = false;
    ARRAY_TYPE *value = (ARRAY_TYPE *)item.value;  // 数组格式
    item.opcode = strtol(item.name, NULL, 16);  
    switch (item.opcode) {  
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK:
        APP_USER_LOGI("GEN_ONOFF_SET: ONOFF: %d", value[0]);
        for (uint8_t i = 0; i < item.addr_num; i++) {
            ret = ble_mesh_onoff_set_state(item.opcode, item.addr[i], value[0]);
        }
        break;
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET:
        APP_USER_LOGI("GEN_ONOFF_GET: ONOFF");
        ret = ble_mesh_onoff_get(item.addr[0]);
        break;
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET:
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK: {
        esp_ble_mesh_state_change_light_hsl_set_t *hsl = (esp_ble_mesh_state_change_light_hsl_set_t *)value;
        APP_USER_LOGI("LIGHT_HSL_SET: HSL: %d %d %d", hsl->hue, hsl->saturation, hsl->lightness);
        for (uint8_t i = 0; i < item.addr_num; i++) {
            ret = ble_mesh_light_hsl_set_state(item.opcode, item.addr[i], *hsl);
        }
        break;
    }
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_GET:
        APP_USER_LOGI("LIGHT_HSL_GET");
        ret = ble_mesh_light_hsl_get(item.addr[0]); 
        break;
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET:
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK: {
        esp_ble_mesh_state_change_light_ctl_set_t *ctl = (esp_ble_mesh_state_change_light_ctl_set_t *)value;
        ctl->delta_uv = 0;
        APP_USER_LOGI("LIGHT_CTL_SET: lightness %d, temperature %d", ctl->lightness, ctl->temperature); 
        for (uint8_t i = 0; i < item.addr_num; i++) {
            ret = ble_mesh_light_ctl_set_state(item.opcode, item.addr[i], *ctl);
        }
        break;
    }
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_GET:
        APP_USER_LOGI("LIGHT_CTL_GET");
        ret = ble_mesh_light_ctl_get(item.addr[0]);
        break;
    case MESH_MODEL_OP_ATTR_SET:  
    case MESH_MODEL_OP_ATTR_SET_UNACK:  
    case MESH_MODEL_OP_ATTR_GET:   
        esp_log_buffer_hex("GENIE_MODEL_OP_ATTR_SET", (ARRAY_TYPE *)item.value, item.size);   
        for (uint8_t i = 0; i < item.addr_num; i++) {
            ret = ble_mesh_send_vendor_message(item.addr[i], item.opcode, (ARRAY_TYPE *)item.value, item.size); 
        }
        break;
    default:
        break;
    }
 
    if (ret == true) {
        APP_USER_LOGI("ack mesh_set_opcode = %s", item.name);
        //app_upper_cloud_format(item.addr, mid_value, item.name, item.value, item.size);  // 放ble_mesh接收函数异步应答
    }

    return NULL;
}

static void ble_mesh_switch_hanlder(uint8_t key_value);

static char *mode_handler(app_parse_data_t item)
{
    uint8_t mode = item.name[4] - '0';  // "mode1"
    if (mode > 1) {
        char *data = cJSON_PrintUnformatted(item.json);
        if (strlen(data) > 15) {  // 空指令不执行存储！ {"mode2":{}}
            ESP_LOGI(TAG, "%s = %s", item.name, data);
            scene_write(item.name, data);
        }
        cJSON_free(data);
    }
    ble_mesh_switch_hanlder(1 << mode);
    return "ok";
} 

static char *group_handler(app_parse_data_t item)
{
    ble_mesh_config_model_sub_add(item.addr[0], 0xF100); 
    return "ok";
} 

// uint8_t arr[] ={ 0xaa,0xaa,0x11,0x11};
typedef struct {
    const char *name;
    char *(*handler)(app_parse_data_t);
} json_handler_t;

static const json_handler_t mqtt_handler_table[] = {
    /*********** 系 统 指 令 ***********/
    { "0x",         mesh_handler        },
    { "bed",        bed_handler         },
    { "mode",       mode_handler        },   // 情景模式
    { "group",      group_handler       },   // 添加分组
    { "ver",        ver_handler         },
    { "sntp",       sntp_handler        },
    { "ota",        ota_handler         },
    { "bind",       bind_handler        },
    { "unbind",     unbind_handler      },
    { "clear",      clear_handler       },
}; 
#define SYS_TABLE_SIZE    (sizeof(mqtt_handler_table) / sizeof(json_handler_t))
//========================================================================================
//========================================================================================
/*
本地默认情景模式：
初始模式[1]： 全部灯光打开、窗帘打开、智能床零压模式。
睡眠模式[2]： 全部灯光熄灭、窗帘关闭、智能床深睡模式。
起床模式[3]： 床低灯光打开、窗帘打开10%、智能床零压模式。
观影模式[4]： 观影灯光打开、窗帘关闭、智能床观影模式。
阅读模式[5]： 床头灯关打开、窗帘关闭、智能床阅读模式。*/
// 解析云端数据任务
static void app_parse_cloud_handler(char *data)
{
    cJSON *jroot = cJSON_Parse(data);    
    if (jroot == NULL) {  // ERROR!!!
        #ifdef APP_USER_DEBUG_ENABLE  // debug
        APP_USER_LOGE("Sock cJSON_Parse error");
        #endif
        return;
    }
 
#if APP_CONFIG_MQTT_ENABLE
    cJSON *mid_item = cJSON_GetObjectItem(jroot, "mid");
    if (mid_item != NULL) {  
        strcpy(mid_value, mid_item->valuestring);  // 得到消息ID
        cJSON_DeleteItemFromObject(jroot, "mid");   // 删除"mid"节点
    }
#endif

    app_parse_data_t parse = { 0 };

    cJSON *addr_item = cJSON_GetObjectItem(jroot, "addr");
    if (addr_item != NULL) {
        if (addr_item->type == cJSON_String) {  // 单播/组地址
            parse.addr[0] = strtol(addr_item->valuestring, NULL, 16); 
            parse.addr_num = 1;
        } else if (addr_item->type == cJSON_Number) { 
            parse.addr[0] = addr_item->valueint;
            parse.addr_num = 1;
        } else if (addr_item->type == cJSON_Array) {
            parse.addr_num  = cJSON_GetArraySize(addr_item);
            for (uint8_t i = 0; i < parse.addr_num; i++) {
                cJSON *item = cJSON_GetArrayItem(addr_item, i);  // 取值
                parse.addr[i] = item->valueint;
            }
        } 

        esp_log_buffer_hex("parse.addr", parse.addr, parse.addr_num);
        cJSON_DeleteItemFromObject(jroot, "addr"); // 删除"addr"节点

    }  
 
    /* ***************************** 功能指令集解析 ************************* */
    for (uint8_t index = 0; index < cJSON_GetArraySize(jroot); index++) {
        cJSON *item = cJSON_GetArrayItem(jroot, index);
        if (cJSON_IsNull(item) || parse.addr_num == 0)  break; // 为空
        //========================================================================================
        for (uint8_t i = 0; i < SYS_TABLE_SIZE; i++) {  // 遍历键名
            if (strncmp(item->string, mqtt_handler_table[i].name, strlen(mqtt_handler_table[i].name)) == 0) {
                switch (item->type) {
                case cJSON_String:
                    parse.size  = 0;  // 字符串类似长度为0
                    parse.value = item->valuestring;
                    parse.name  = item->string;
                    APP_USER_LOGI("%s_handler: %s", parse.name, (char *)parse.value);
                    break;
                case cJSON_Array:   
                    parse.name  = item->string;
                    parse.size  = cJSON_GetArraySize(item);
                    parse.value = (ARRAY_TYPE *)malloc(parse.size * sizeof(ARRAY_TYPE));
                    for (uint8_t i = 0; i < parse.size; i++) {
                        cJSON *aitem = cJSON_GetArrayItem(item, i);  // 取值
                        ((ARRAY_TYPE *)parse.value)[i] = aitem->valueint;
                    }
                    APP_USER_LOGI("%s_handler: %d", parse.name, ((ARRAY_TYPE *)parse.value)[0]);
                    break;
                case cJSON_Object:  // 可能是情景模式
                    parse.json = jroot;
                    parse.name = item->string;
                    break;
                default:  // error type
                    break;
                }    

                const char *result = mqtt_handler_table[i].handler(parse);
                if (result != NULL) {  // 有数据就响应！
                    APP_USER_LOGI("result = %s", result);
                    app_upper_cloud_format(parse.addr[0], mid_value, item->string, (char *)result, 0);
                }
                if (item->type == cJSON_Array) {  /** 是数组类型记得释放内存!!! */
                    free(parse.value);  // 记得释放内存
                }

                break;
            }
        }
        //========================================================================================
    }
    /* Remember to free memory */
    cJSON_Delete(jroot);  
    jroot = NULL;
}

static void mqtt_subscribe_callback(mqtt_data_t info)
{ 
    // APP_USER_LOGI("Free heap, current: %d, minimum: %d", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());  // 打印内存
    APP_USER_LOGI("mqtt_subscribe_callback->data: %s | %d", info.data, info.len);
    app_parse_cloud_handler((char *)info.data);  
}

//=====================================================================================
//=====================================================================================
// BLE 配网
static const char *separator = "|";   // 分隔符

// {"wifi":"<SSID>|<PASSWORD>"} // {"wifi":"yiree-zhulao|yiree2021"}
// {"wifi":"ok/fail"}    
static char *wifi_handler(char *data)
{
    char *temp = strtok(data, separator);
    for (uint8_t index = 0; temp != NULL; index++) {
        uint8_t len = strlen(temp);  
        APP_USER_LOGI("wifi_temp[%d] = %s | %d", index, temp, len);
        switch (index) {
        case 0:
            memcpy(nvs_wifi.ssid, temp, len); 
            nvs_wifi.ssid[len] = '\0';  
            break;
        case 1:
            memcpy(nvs_wifi.password, temp, len);  
            nvs_wifi.password[len] = '\0'; 
            break;   
        default:
            break;
        }
        temp = strtok(NULL, separator);
    }
    app.audio_voice = VOICE_COMM_WIFI_PAIR;  // 开始配网
    if (wifi_sta_reconnect(nvs_wifi.ssid, nvs_wifi.password) == true) {  // 连接成功了！
        nvs_wifi.status = WIFI_CONFIG_WIFI;         // WIFI配网成功 
        nvs_wifi.addr = ROOT_OWN_ADDR;              // 网关的单播地址
        nvs_wifi_handle(&nvs_wifi, NVS_READWRITE);  // 存储NVS
        xEventGroupSetBits(xEvent, BIT0);           // 配网成功, 切换到BLE_MESH程序
        app.audio_voice = VOICE_COMM_WIFI_PAIR_OK;  // 配网成功
        return "ok";
    } else {
        app.audio_voice = VOICE_COMM_WIFI_PAIR_FAIL;  // 配网失败
        return "fail";
    }
}
 
typedef struct {
    const char *name;
    char *(*handler)(char *);
} ble_gatts_handler_t; 
static const ble_gatts_handler_t ble_gatts_handler_table[] = {
    { "wifi",       wifi_handler        },
}; 

#define BLE_GATTS_TABLE_SIZE    (sizeof(ble_gatts_handler_table) / sizeof(ble_gatts_handler_t))

// BLE_GATTS接收回调函数
void ble_gatts_recv_callback(uint8_t idx, uint8_t *data, uint8_t len)
{
    APP_USER_LOGI("ble_gatts_recv_callback = %s | %d", data, len);

    cJSON *jroot = cJSON_Parse((char *)data);
    if (jroot == NULL) {  // ERROR!!!
        APP_USER_LOGE("BLE cJSON_Parse error");
        return;
    }
 
    for (uint8_t i = 0; i < BLE_GATTS_TABLE_SIZE; i++) {
        cJSON *item = cJSON_GetObjectItem(jroot, ble_gatts_handler_table[i].name);
        if (item != NULL) { 
            const char *result = ble_gatts_handler_table[i].handler(item->valuestring);
            if (result != NULL) {  // 有数据就响应！
                cJSON_ReplaceItemInObject(jroot, item->string, cJSON_CreateString(result));
                char *cjson_data = cJSON_PrintUnformatted(jroot);
                #ifdef APP_USER_DEBUG_ENABLE     
                APP_USER_LOGI("gatts_result = %s", cjson_data);
                #endif
                ble_gatts_write_char((uint8_t *)cjson_data, strlen(cjson_data)); 
                cJSON_free(cjson_data);
            }
            break; 
        }
    }
 
    /* Remember to free memory */
    cJSON_Delete(jroot);  
    jroot = NULL;
}

static void ble_mesh_switch_hanlder(uint8_t key_value)
{
    APP_USER_LOGI("key_value = 0x%02x", key_value);

    uint8_t mode = 0;
    for (mode = 0; mode < 8; mode++) {
        if (key_value & (1 << mode)) {
            break;
        }
    }

    APP_USER_LOGI("key_mode%d = %d", mode, mode);

    // 睡眠模式[0]： 全部灯光熄灭、窗帘关闭、智能床深睡模式。（不能改）
    // 全开模式[1]： 全部灯光打开、窗帘打开、智能床零压模式。（不能改）
    switch (mode) {
    case 0:
        ble_mesh_onoff_set_unack(MESH_ALL_GROUP_ADDR, false);
        rf_motor_write((uint8_t[])RF_MOTOR_MODE_SLEEP, 2);
        break;
    case 1:
        ble_mesh_onoff_set_unack(MESH_ALL_GROUP_ADDR, true);
        rf_motor_write((uint8_t[])RF_MOTOR_MODE_ZERO, 2);
        break;
    default:
        char name[12];
        sprintf(name, "mode%d", mode);
        app_scene_action(name);  // 执行情景
        break;
    }
 
}

//===================================================================================================================
//===================================================================================================================
void ble_mesh_recv_callback(mesh_transfer_t param)
{
    #ifdef APP_USER_DEBUG_ENABLE
    APP_USER_LOGI("<ble_mesh_recv_callback> unicast_addr = 0x%04x, opcode = 0x%06lx", param.unicast_addr, param.opcode);
    #endif
    switch (param.opcode) {
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET:
    case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS: {
        param.opcode = ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS;
        APP_USER_LOGI("OP_GEN_ONOFF: %d", param.data[0]);
        break;
    }
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET:
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_GET:
    case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS: {
        param.opcode = ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS;
        esp_ble_mesh_state_change_light_hsl_set_t *hsl = (esp_ble_mesh_state_change_light_hsl_set_t *)param.data;
        APP_USER_LOGI("_OP_LIGHT_HSL: %X %X %X", hsl->hue, hsl->saturation, hsl->lightness);
        break;
    }
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET:    
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_GET:
    case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS: {
        param.opcode = ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS;
        param.len = 4;  // 这里只上报4Byte数据
        esp_ble_mesh_state_change_light_ctl_set_t *ctl = (esp_ble_mesh_state_change_light_ctl_set_t *)param.data;
        APP_USER_LOGI("OP_LIGHT_CTL-> lightness 0x%X, temperature 0x%X", ctl->lightness, ctl->temperature);
        break;
    }
    case MESH_MODEL_OP_ATTR_SET:
    case MESH_MODEL_OP_ATTR_GET:
    case MESH_MODEL_OP_ATTR_STATUS:
        param.opcode = MESH_MODEL_OP_ATTR_STATUS;
        esp_log_buffer_hex("ble_mesh_vnd_data", param.data, param.len);
        APP_USER_LOGI("ble_mesh_vnd_data: %s | %d", param.data, param.len);
        if (!memcmp(param.data, "key", 3)) {  // 是按键发过来的指令
            ble_mesh_switch_hanlder(param.data[3]);
            return;
        }
        break;
    default:
        param.len = 0;
        APP_USER_LOGW("<%s>,  default->opcode = 0x%06lx", __func__, param.opcode);
        break;
    }

    if (param.len) {
        char opcode[8];
        sprintf(opcode, "0x%0lX", param.opcode);
        app_upper_cloud_format(param.unicast_addr, mid_value, opcode, param.data, param.len);
    }
    return;
}


//===================================================================================================================
//===================================================================================================================
void uart_recv_callback(uart_port_t uart_port, uint8_t *data, uint16_t lenght)
{
    // APP_USER_LOGI("uart_callback[%d]: %.*s | %d", uart_port, lenght, data, lenght);
    esp_log_buffer_hex("uart_read", data, lenght);
    APP_USER_LOGI("uart_callback[%d] len = %d", uart_port, lenght); 
}

//===================================================================================================================
//===================================================================================================================
// BLE初始化任务
void app_ble_init_task(void *arg)
{
    xEvent = xEventGroupCreate();
    /***************************** Bluedroid LE ****************************** */
    /* 注意： BLE 要在WIFI之前先初始化，不然会报错：coexist: [2202407] Error! Should enable WiFi modem sleep when both WiFi and Bluetooth a */
    ESP_ERROR_CHECK( bluedroid_stack_init() );    // 初始化"BLE协议栈" 
 
    if (get_sys_config_network() == false) {  // 说明没有配网
        ble_gatts_init();
        ble_gatts_register_callback(ble_gatts_recv_callback);
        xEventGroupWaitBits(xEvent, BIT0, pdFALSE, pdFALSE, portMAX_DELAY);
        vTaskDelay(500);             // 等待GATTS事情处理完
        ble_gatts_deinit_handle();   // 先注销GATTS
        vTaskDelay(2000);            // 延时等待注销完成
    } 

    ble_mesh_init();
    ble_mesh_register_callback(ble_mesh_recv_callback); 
     
    vEventGroupDelete(xEvent);
    vTaskDelete(NULL);
}

//===================================================================================================================
//===================================================================================================================
void app_user_init(void) 
{
    APP_USER_LOGI("app_user_init...");
    mid_value[0] = '0';
    app_get_self_info(&self);  // 读设备自己的信息 
    nvs_user_handle(&nvs_user, NVS_READONLY);   // 读用户信息
    APP_USER_LOGI("nvs_user.rfid = 0x%08lx", nvs_user.rfid.u32);
    nvs_wifi_handle(&nvs_wifi, NVS_READONLY);   // 先读WIFI信息,获取配网状态
    nvs_wifi.addr = ROOT_OWN_ADDR;  // 网关的单播地址

#if 1       
    hal_gpio_init();
    hal_a7106_init();
    hal_exti_init(exit_isr_callback);
    hal_usb_msc_init();   // 初始化U盘（USB + SDCard + FileSystem）
    audio_adf_init();
    xTaskCreatePinnedToCore(app_user_task, "app_user", 4 * 1024, NULL, 5, NULL, APP_CPU_NUM);
    vTaskDelay(200);  // 等待硬件外设初始化完成！ 释放一些内存再初始化BLE&WIFI
    // return;
#endif
 
    //nvs_wifi.status = 1;   // test...
 
    /***************************** Bluedroid LE ****************************** */
    xTaskCreatePinnedToCore(app_ble_init_task, "ble_init", 3 * 1024, NULL, 3, NULL, APP_CPU_NUM);
    vTaskDelay(200);  // 等待BLE协议初始化！才能初始化WIFI，不然会报错！
    //return;
 
    /***************************** Wi-Fi *******************************/
    app_wifi_init(nvs_wifi.ssid, nvs_wifi.password);
    app_mqtt_init();  
    mqtt_subscribe_register_callback(mqtt_subscribe_callback);
    /* END...................................................................................*/
}
/* END...................................................................................*/
// 查看错误码信息： https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/error-codes.html
// http://wiki.tjc1688.com/doku.php 
